// Camera class mostly taken from the book pages 336 - 338

#include "Camera.h"

Camera::Camera()
{
    eye.set(2.0,2.0,-2.0);
    look.set(0.0,0.0,0.0);
    up.set(0.0,1.0,0.0);
    set(eye.getX(), eye.getY(), eye.getZ(), look.getX(), look.getY(), look.getZ(), up.getX(), up.getY(), up.getZ());
}

void Camera::set(Point3 Eye, Point3 lookAt, Vector3 up)
{ // create a modelview matrix and send it to OpenGL
    eye.set(Eye); // store the given eye position
    n.set(eye.getX() - lookAt.getX(), eye.getY() - lookAt.getY(), eye.getZ() - lookAt.getZ()); // make n
    u.set(up.crossProd(n)); // make u = up X n
    n.normalize(); u.normalize(); // make them unit length
    v.set(n.crossProd(u)); //make v = n X u
    setModelViewMatrix(); // tell OpenGL
}

void Camera::set(float EyeX, float EyeY, float EyeZ, float lookAtX, float lookAtY, float lookAtZ, float upX, float upY, float upZ)
{ // create a modelview matrix and send it to OpenGL
    Point3 Eye(EyeX, EyeY, EyeZ);
    Point3 lookAt(lookAtX, lookAtY, lookAtZ);
    Vector3 up(upX, upY, upZ);

    eye.set(Eye); // store the given eye position
    n.set(eye.getX() - lookAt.getX(), eye.getY() - lookAt.getY(), eye.getZ() - lookAt.getZ()); // make n
    u.set(up.crossProd(n)); // make u = up X n
    n.normalize(); u.normalize(); // make them unit length
    v.set(n.crossProd(u)); //make v = n X u
    setModelViewMatrix(); // tell OpenGL
}

void Camera::rotate(Vector3 axis, float angle)
{
    float cs = cos(3.14159265/180 * angle);
    float sn = sin(3.14159265/180 * angle);
    //axis.normalize();

    if (axis.getX() == 1)
    {
        Vector3 t(v);
        v.set(cs * t.getX() + sn * n.getX(), cs * t.getY() + sn * n.getY(), cs * t.getZ() + sn * n.getZ());
        //n.set(sn *t.getX() + cs * n.getX(), sn * t.getY() + cs * n.getY(), sn * t.getZ() + cs * n.getZ());
        n.set(u.crossProd(v));
    }
    if (axis.getY() == 1)
    {
        Vector3 t(u);
        u.set(cs * t.getX() + sn * n.getX(),cs * t.getY() + sn * n.getY(),cs * t.getZ() + sn * n.getZ());
        //n.set(cs * n.getX() + sn * t.getX(),cs * n.getY() + sn * t.getY(),cs * n.getZ() + sn * t.getZ());
        n.set(u.crossProd(v));
    }
    if (axis.getZ() == 1)
    {
        Vector3 t(u);
        u.set(cs * t.getX() - sn * v.getX(), cs * t.getY() - sn * v.getY(), cs * t.getZ() - sn * v.getZ());
        //v.set(sn * t.getX() + cs * v.getX(), sn * t.getY() + cs * v.getY(), sn * v.getZ() + cs * v.getZ());
        v.set(n.crossProd(u));
    }

    setModelViewMatrix();
}

//rotate the camera around its local z axis
void Camera::roll(float angle)
{    // roll the camera through angle degrees
    //convert degrees to radians
    float cs = cos(3.14159265/180 * angle);
    float sn = sin(3.14159265/180 * angle);
    Vector3 t(u); // remember old u

    u.set(cs * t.getX() - sn * v.getX(), cs * t.getY() - sn * v.getY(), cs * t.getZ() - sn * v.getZ());
    //v.set(sn * t.getX() + cs * v.getX(), sn * t.getY() + cs * v.getY(), sn * v.getZ() + cs * v.getZ());
    v.set(n.crossProd(u));
    setModelViewMatrix();
}

//rotate the camera around its local x axis
void Camera::pitch(float angle)
{    // roll the camera through angle degrees
    //convert degrees to radians
    float cs = cos(3.14159265/180 * angle);
    float sn = sin(3.14159265/180 * angle);
    Vector3 t(v);
    v.set(cs * t.getX() + sn * n.getX(), cs * t.getY() + sn * n.getY(), cs * t.getZ() + sn * n.getZ());
    //n.set(sn *t.getX() + cs * n.getX(), sn * t.getY() + cs * n.getY(), sn * t.getZ() + cs * n.getZ());
    n.set(u.crossProd(v));
    setModelViewMatrix();
}

//rotate the camera around its local y axis
void Camera::yaw(float angle)
{    // roll the camera through angle degrees
    //convert degrees to radians
    float cs = cos(3.14159265/180 * angle);
    float sn = sin(3.14159265/180 * angle);
    Vector3 t(u);
    u.set(cs * t.getX() + sn * n.getX(),cs * t.getY() + sn * n.getY(),cs * t.getZ() + sn * n.getZ());
    //n.set(cs * n.getX() + sn * t.getX(),cs * n.getY() + sn * t.getY(),cs * n.getZ() + sn * t.getZ());
    n.set(u.crossProd(v));
    setModelViewMatrix();
}


void Camera::slide(float delU, float delV, float delN)
{
    eye.setX(eye.getX() + delU * u.getX() + delV * v.getX() + delN * n.getX());
    eye.setY(eye.getY() + delU * u.getY() + delV * v.getY() + delN * n.getY());
    eye.setZ(eye.getZ() + delU * u.getZ() + delV * v.getZ() + delN * n.getZ());

    look.setX(look.getX() + delU * u.getX() + delV * v.getX() + delN * n.getX());
    look.setY(look.getY() + delU * u.getY() + delV * v.getY() + delN * n.getY());
    look.setZ(look.getZ() + delU * u.getZ() + delV * v.getZ() + delN * n.getZ());

    setModelViewMatrix();
}


void Camera::setShape(float viewAngle,float asp,float nearD,float farD)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(viewAngle,asp,nearD,farD);
}

void Camera::setModelViewMatrix()
{ // load modelview matrix with existing camera values
    float m[16];

    Vector3 eVec(eye.getX(),eye.getY(),eye.getZ()); // constructor of vector version of eye

    m[0] = u.getX(); m[4] = u.getY(); m[8] = u.getZ(); m[12] = -eVec.dotProd(u);
    m[1] = v.getX(); m[5] = v.getY(); m[9] = v.getZ(); m[13] = -eVec.dotProd(v);
    m[2] = n.getX(); m[6] = n.getY(); m[10]= n.getZ(); m[14] = -eVec.dotProd(n);
    m[3] = 0       ; m[7] = 0;        m[11] = 0;       m[15] = 1.0;

    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(m); // load OpenGL's modelview matrix
}

